//	Simple test cases for the threads assignment.
//

#include "copyright.h"
#include "system.h"
#include "iostream.h"
//#include "Assignment1.h"





#ifdef CHANGED
#include "synch.h"
#endif

/*extern void Patient(int patientId);
extern void Receptionist(int rec_id);
extern void InitilalizeRecep(int number);
extern void InitializeDB(int number);
extern void DoorBoy(int id);
extern void Doctor(int id);
extern void Hospital_Manager(int HM_id);
extern void InitializeManager();
extern void InitializeDoctor(int number);
extern void InitializePatient(int number);
extern void InitializeCashier(int number);
extern void InitializePharma(int number);
extern void Cashier(int number);
extern void PharmacyClerk(int number);*/


#ifdef CHANGED

// --------------------------------------------------
// Test Suite
// --------------------------------------------------


// --------------------------------------------------
// Test 1 - see TestSuite() for details
// --------------------------------------------------
Semaphore t1_s1("t1_s1",0);       // To make sure t1_t1 acquires the
                                  // lock before t1_t2
Semaphore t1_s2("t1_s2",0);       // To make sure t1_t2 Is waiting on the 
                                  // lock before t1_t3 releases it
Semaphore t1_s3("t1_s3",0);       // To make sure t1_t1 does not release the
                                  // lock before t1_t3 tries to acquire it
Semaphore t1_done("t1_done",0);   // So that TestSuite knows when Test 1 is
                                  // done
Lock t1_l1("t1_l1");		  // the lock tested in Test 1

// --------------------------------------------------
// t1_t1() -- test1 thread 1
//     This is the rightful lock owner
// --------------------------------------------------
void t1_t1() {
    t1_l1.Acquire();
    t1_s1.V();  // Allow t1_t2 to try to Acquire Lock
 
    printf ("%s: Acquired Lock %s, waiting for t3\n",currentThread->getName(),
	    t1_l1.getName());
    t1_s3.P();
    printf ("%s: working in CS\n",currentThread->getName());
    for (int i = 0; i < 1000000; i++) ;
    printf ("%s: Releasing Lock %s\n",currentThread->getName(),
	    t1_l1.getName());
    t1_l1.Release();
    t1_done.V();
}

// --------------------------------------------------
// t1_t2() -- test1 thread 2
//     This thread will wait on the held lock.
// --------------------------------------------------
void t1_t2() {

    t1_s1.P();	// Wait until t1 has the lock
    t1_s2.V();  // Let t3 try to acquire the lock

    printf("%s: trying to acquire lock %s\n",currentThread->getName(),
	    t1_l1.getName());
    t1_l1.Acquire();

    printf ("%s: Acquired Lock %s, working in CS\n",currentThread->getName(),
	    t1_l1.getName());
    for (int i = 0; i < 10; i++)
	;
    printf ("%s: Releasing Lock %s\n",currentThread->getName(),
	    t1_l1.getName());
    t1_l1.Release();
    t1_done.V();
}

// --------------------------------------------------
// t1_t3() -- test1 thread 3
//     This thread will try to release the lock illegally
// --------------------------------------------------
void t1_t3() {

    t1_s2.P();	// Wait until t2 is ready to try to acquire the lock

    t1_s3.V();	// Let t1 do it's stuff
    for ( int i = 0; i < 3; i++ ) {
	printf("%s: Trying to release Lock %s\n",currentThread->getName(),
	       t1_l1.getName());
	t1_l1.Release();
    }
}

// --------------------------------------------------
// Test 2 - see TestSuite() for details
// --------------------------------------------------
Lock t2_l1("t2_l1");		// For mutual exclusion
Condition t2_c1("t2_c1");	// The condition variable to test

Semaphore t2_s1("t2_s1",0);	// To ensure the Signal comes before the wait
Semaphore t2_done("t2_done",0);     // So that TestSuite knows when Test 2 is
                                  // done

// --------------------------------------------------
// t2_t1() -- test 2 thread 1
//     This thread will signal a variable with nothing waiting
// --------------------------------------------------
void t2_t1() {
    t2_l1.Acquire();
    printf("%s: Lock %s acquired, signalling %s\n",currentThread->getName(),t2_l1.getName(),t2_c1.getName());
    t2_c1.Signal(&t2_l1);
    //printf("%s: Releasing Lock %s\n",currentThread->getName(),t2_l1.getName());
    t2_l1.Release();
    t2_s1.V();	// release t2_t2
    t2_done.V();
}

// --------------------------------------------------
// t2_t2() -- test 2 thread 2
//     This thread will wait on a pre-signalled variable
// --------------------------------------------------
void t2_t2() {
	printf("Inside t2_t2");
    t2_s1.P();	// Wait for t2_t1 to be done with the lock
    
    t2_l1.Acquire();
   printf("%s: Lock %s acquired, waiting on %s\n",currentThread->getName(),t2_l1.getName(), t2_c1.getName());
   t2_c1.Wait(&t2_l1);
   printf("%s: Releasing Lock %s\n",currentThread->getName(),t2_l1.getName());
    t2_l1.Release();
}
// --------------------------------------------------
// Test 3 - see TestSuite() for details
// --------------------------------------------------
Lock t3_l1("t3_l1");		// For mutual exclusion
Condition t3_c1("t3_c1");	// The condition variable to test
Semaphore t3_s1("t3_s1",0);	// To ensure the Signal comes before the wait
Semaphore t3_done("t3_done",0); // So that TestSuite knows when Test 3 is
                                // done

// --------------------------------------------------
// t3_waiter()
//     These threads will wait on the t3_c1 condition variable.  Only
//     one t3_waiter will be released
// --------------------------------------------------
void t3_waiter() {
    t3_l1.Acquire();
    t3_s1.V();		// Let the signaller know we're ready to wait
    printf("%s: Lock %s acquired, waiting on %s\n",currentThread->getName(),
	   t3_l1.getName(), t3_c1.getName());
    t3_c1.Wait(&t3_l1);
    printf("%s: freed from %s\n",currentThread->getName(), t3_c1.getName());
    t3_l1.Release();
    t3_done.V();
}


// --------------------------------------------------
// t3_signaller()
//     This threads will signal the t3_c1 condition variable.  Only
//     one t3_signaller will be released
// --------------------------------------------------
void t3_signaller() {

    // Don't signal until someone's waiting
    
    for ( int i = 0; i < 5 ; i++ ) 
	t3_s1.P();
    t3_l1.Acquire();
    printf("%s: Lock %s acquired, signalling %s\n",currentThread->getName(),
	   t3_l1.getName(), t3_c1.getName());
    t3_c1.Signal(&t3_l1);
    printf("%s: Releasing %s\n",currentThread->getName(), t3_l1.getName());
    t3_l1.Release();
    t3_done.V();
}
 
// --------------------------------------------------
// Test 4 - see TestSuite() for details
// --------------------------------------------------
Lock t4_l1("t4_l1");		// For mutual exclusion
Condition t4_c1("t4_c1");	// The condition variable to test
Semaphore t4_s1("t4_s1",0);	// To ensure the Signal comes before the wait
Semaphore t4_done("t4_done",0); // So that TestSuite knows when Test 4 is
                                // done

// --------------------------------------------------
// t4_waiter()
//     These threads will wait on the t4_c1 condition variable.  All
//     t4_waiters will be released
// --------------------------------------------------
void t4_waiter() {
    t4_l1.Acquire();
    t4_s1.V();		// Let the signaller know we're ready to wait
    printf("%s: Lock %s acquired, waiting on %s\n",currentThread->getName(),
	   t4_l1.getName(), t4_c1.getName());
    t4_c1.Wait(&t4_l1);
    printf("%s: freed from %s\n",currentThread->getName(), t4_c1.getName());
    t4_l1.Release();
    t4_done.V();
}


// --------------------------------------------------
// t2_signaller()
//     This thread will broadcast to the t4_c1 condition variable.
//     All t4_waiters will be released
// --------------------------------------------------
void t4_signaller() {

    // Don't broadcast until someone's waiting
    
    for ( int i = 0; i < 5 ; i++ ) 
	t4_s1.P();
    t4_l1.Acquire();
    printf("%s: Lock %s acquired, broadcasting %s\n",currentThread->getName(),
	   t4_l1.getName(), t4_c1.getName());
    t4_c1.Broadcast(&t4_l1);
    printf("%s: Releasing %s\n",currentThread->getName(), t4_l1.getName());
    t4_l1.Release();
    t4_done.V();
}
// --------------------------------------------------
// Test 5 - see TestSuite() for details
// --------------------------------------------------
Lock t5_l1("t5_l1");		// For mutual exclusion
Lock t5_l2("t5_l2");		// Second lock for the bad behavior
Condition t5_c1("t5_c1");	// The condition variable to test
Semaphore t5_s1("t5_s1",0);	// To make sure t5_t2 acquires the lock after
                                // t5_t1

// --------------------------------------------------
// t5_t1() -- test 5 thread 1
//     This thread will wait on a condition under t5_l1
// --------------------------------------------------
void t5_t1() {
    t5_l1.Acquire();
    t5_s1.V();	// release t5_t2
    printf("%s: Lock %s acquired, waiting on %s\n",currentThread->getName(),
	   t5_l1.getName(), t5_c1.getName());
    t5_c1.Wait(&t5_l1);
    printf("%s: Releasing Lock %s\n",currentThread->getName(),
	   t5_l1.getName());
    t5_l1.Release();
}

// --------------------------------------------------
// t5_t1() -- test 5 thread 1
//     This thread will wait on a t5_c1 condition under t5_l2, which is
//     a Fatal error
// --------------------------------------------------
void t5_t2() {
    t5_s1.P();	// Wait for t5_t1 to get into the monitor
    t5_l1.Acquire();
    t5_l2.Acquire();
    printf("%s: Lock %s acquired, signalling %s\n",currentThread->getName(),
	   t5_l2.getName(), t5_c1.getName());
    t5_c1.Signal(&t5_l2);
    printf("%s: Releasing Lock %s\n",currentThread->getName(),
	   t5_l2.getName());
    t5_l2.Release();
    printf("%s: Releasing Lock %s\n",currentThread->getName(),
	   t5_l1.getName());
    t5_l1.Release();
}

// --------------------------------------------------
// TestSuite()
//     This is the main thread of the test suite.  It runs the
//     following tests:
//
//       1.  Show that a thread trying to release a lock it does not
//       hold does not work
//
//       2.  Show that Signals are not stored -- a Signal with no
//       thread waiting is ignored
//
//       3.  Show that Signal only wakes 1 thread
//
//	 4.  Show that Broadcast wakes all waiting threads
//
//       5.  Show that Signalling a thread waiting under one lock
//       while holding another is a Fatal error
//
//     Fatal errors terminate the thread in question.
// --------------------------------------------------
/*
void TestSuite(void) {
    Thread *t;
    char *name;
    int i;
    

    
    
    // Test 1

    printf("Starting Test 1\n");

    t = new Thread("t1_t1");
    t->Fork((VoidFunctionPtr)t1_t1,0);

    t = new Thread("t1_t2");
    t->Fork((VoidFunctionPtr)t1_t2,0);

    t = new Thread("t1_t3");
    t->Fork((VoidFunctionPtr)t1_t3,0);

    // Wait for Test 1 to complete
    for (  i = 0; i < 2; i++ )
	t1_done.P();

    // Test 2

    printf("Starting Test 2.  Note that it is an error if thread t2_t2\n");
    printf("completes\n");

    t = new Thread("t2_t1");
    t->Fork((VoidFunctionPtr)t2_t1,0);

    t = new Thread("t2_t2");
    t->Fork((VoidFunctionPtr)t2_t2,0);

    // Wait for Test 2 to complete
    t2_done.P();

    // Test 3

    printf("Starting Test 3\n");

    for (  i = 0 ; i < 5 ; i++ ) {
	name = new char [20];
	sprintf(name,"t3_waiter%d",i);
	t = new Thread(name);
	t->Fork((VoidFunctionPtr)t3_waiter,0);
    }
    t = new Thread("t3_signaller");
    t->Fork((VoidFunctionPtr)t3_signaller,0);

    // Wait for Test 3 to complete
    for (  i = 0; i < 2; i++ )
	t3_done.P();

    // Test 4

    printf("Starting Test 4\n");

    for (  i = 0 ; i < 5 ; i++ ) {
	name = new char [20];
	sprintf(name,"t4_waiter%d",i);
	t = new Thread(name);
	t->Fork((VoidFunctionPtr)t4_waiter,0);
    }
    t = new Thread("t4_signaller");
    t->Fork((VoidFunctionPtr)t4_signaller,0);

    // Wait for Test 4 to complete
    for (  i = 0; i < 6; i++ )
	t4_done.P();

    // Test 5

    printf("Starting Test 5.  Note that it is an error if thread t5_t1\n");
    printf("completes\n");

    t = new Thread("t5_t1");
    t->Fork((VoidFunctionPtr)t5_t1,0);

    t = new Thread("t5_t2");
    t->Fork((VoidFunctionPtr)t5_t2,0);

}


void simulate(int patient, int receptionist, int doorboy, int doctor, int cashier, int clerks, int manager, int testCaseNumber);

void test1(){
    printf("\n\nStarting Test 1\n\n");
    cout<<"This test show simulation without any patients.\n";
    cout<<"We don't create threads for the patient hence  go on break.\n";
    cout<<"We do not fork the doorboy thread.Since there are no doorboys,the patient cannot go in\n";
    cout<<"Note that it is an error if the patient goes to meet the doctor.\n";
    simulate(0,3,3,3,3,3,1,1);

}

void test2(int patient, int receptionist, int doorboy, int doctor, int cashier, int clerks, int manager, int testCaseNumber){
    printf("\n\nStarting Test 2\n\n");
    Thread *t1;
    char *name;

     InitilalizeRecep(1);
     InitializeDB(doorboy);
     InitializeDoctor(doctor);
     InitializeManager();
     InitializePatient(patient);
     InitializeCashier(1);
     InitializePharma(1);

    for(int pat=0; pat<patient; pat++)
    {
        name = new char [20];
        sprintf(name,"\nt%d_patient_%d",testCaseNumber,pat);
        t1 = new Thread(name);
        t1->Fork((VoidFunctionPtr)Patient,pat);
    }

//    for(int rec=0;rec<receptionist; rec++)
//    {
//        name = new char [20];
//        sprintf(name,"\nt%d_receptionist_%d",testCaseNumber,rec);
//        t1 = new Thread(name);
//        t1->Fork((VoidFunctionPtr)Receptionist,rec);
//    }

     for(int db=0;db<doorboy; db++)
    {
        name = new char [20];
        sprintf(name,"\nt%d_doorboy_%d",testCaseNumber,db);
        t1 = new Thread(name);
        t1->Fork((VoidFunctionPtr)DoorBoy,db);
    }

      for(int doc=0;doc<doctor; doc++)
    {
        name = new char [20];
        sprintf(name,"\nt%d_doctor_%d",testCaseNumber,doc);
        t1 = new Thread(name);
        t1->Fork((VoidFunctionPtr)Doctor,doc);
    }

      for(int cash=0;cash<cashier; cash++)
    {
        name = new char [20];
        sprintf(name,"\nt%d_cashier_%d",testCaseNumber,cash);
        t1 = new Thread(name);
        t1->Fork((VoidFunctionPtr)Cashier,cash);
    }

       for(int clerk=0;clerk<clerks; clerk++)
    {
        name = new char [20];
        sprintf(name,"\nt%d_pharmacyclerk_%d",testCaseNumber,clerk);
        t1 = new Thread(name);
        t1->Fork((VoidFunctionPtr)PharmacyClerk,clerk);
    }

//      name = new char [20];
//      sprintf(name,"\nt%d_Manager",testCaseNumber);
//      t1 = new Thread(name);
//      t1->Fork((VoidFunctionPtr)Hospital_Manager,1);
}

void test3(){
    simulate(5,3,1,1,1,1,1,3);
}




void Problem2Test(int check)
{
    if(check==1)
        test1();
    else if(check==2)
        test2(10,0,3,3,0,0,1,2);
    else if(check==3)
        test3();
    else
        simulate(15,3,3,3,3,3,1,7);

}

void Problem2(void)
{
    int patient, receptionist, doorboy, doctor, cashier, clerks, manager, testCaseNumber;
    testCaseNumber = 0;
    manager = 1;
    cout<<"\n\n***Enter Parameters***" <<endl;
    cout<<"\nPatients: "<<endl; cin>>patient;
    cout<<"\nReceptionists: "<<endl; cin>>receptionist;
    cout<<"\nDoctor: "<<endl; cin>>doctor;
    doorboy = doctor;
    cout<<"\nCashiers: "<<endl; cin>>cashier;
    cout<<"\nPharmacy Clerks: "<<endl; cin>>clerks;

    simulate(patient, receptionist, doorboy, doctor, cashier, clerks, manager, testCaseNumber);
    //simulate(25,3,3,3,3,3,1,0);

}

void simulate(int patient, int receptionist, int doorboy, int doctor, int cashier, int clerks, int manager, int testCaseNumber)
{

    Thread *t1;
    char *name;

     InitilalizeRecep(receptionist);
     InitializeDB(doorboy);
     InitializeDoctor(doctor);
     InitializeManager();
     InitializePatient(patient);
     InitializeCashier(cashier);
     InitializePharma(clerks);

    for(int pat=0; pat<patient; pat++)
    {
        name = new char [20];
        sprintf(name,"\nt%d_patient_%d",testCaseNumber,pat);
        t1 = new Thread(name);
        t1->Fork((VoidFunctionPtr)Patient,pat);
    }

    for(int rec=0;rec<receptionist; rec++)
    {
        name = new char [20];
        sprintf(name,"\nt%d_receptionist_%d",testCaseNumber,rec);
        t1 = new Thread(name);
        t1->Fork((VoidFunctionPtr)Receptionist,rec);
    }

    for(int doc=0;doc<doctor; doc++)
    {
        name = new char [20];
        sprintf(name,"\nt%d_doctor_%d",testCaseNumber,doc);
        t1 = new Thread(name);
        t1->Fork((VoidFunctionPtr)Doctor,doc);
    }

    for(int db=0;db<doorboy; db++)
    {
        name = new char [20];
        sprintf(name,"\nt%d_doorboy_%d",testCaseNumber,db);
        t1 = new Thread(name);
        t1->Fork((VoidFunctionPtr)DoorBoy,db);
    }

      

      for(int cash=0;cash<cashier; cash++)
    {
        name = new char [20];
        sprintf(name,"\nt%d_cashier_%d",testCaseNumber,cash);
        t1 = new Thread(name);
        t1->Fork((VoidFunctionPtr)Cashier,cash);
    }

       for(int clerk=0;clerk<clerks; clerk++)
    {
        name = new char [20];
        sprintf(name,"\nt%d_pharmacyclerk_%d",testCaseNumber,clerk);
        t1 = new Thread(name);
        t1->Fork((VoidFunctionPtr)PharmacyClerk,clerk);
    }

      name = new char [20];
      sprintf(name,"\nt%d_Manager",testCaseNumber);
      t1 = new Thread(name);
      t1->Fork((VoidFunctionPtr)Hospital_Manager,1);
}*/
#endif
